package org.scohen.juploadr.app.tags;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.PopupList;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.scohen.juploadr.prefs.Configuration;
import org.scohen.juploadr.ui.ReusableUIFactory;
import org.scohen.juploadr.uploadapi.ImageUploadApi;
import org.scohen.juploadr.util.Trie;

import au.com.lastweekend.jim.util.StringUtils;

public class TagsHelper implements VerifyKeyListener, KeyListener {
    private Trie<String, Tag> trie = new Trie<String, Tag>(true);
    private static List<Tag> newTags = new LinkedList<Tag>();
    private boolean initialized = false;
    static {

    }

    public TagsHelper() {
        synchronized (trie) {
            if (!initialized) {
                List tags = ImageUploadApi.getInstance().getCurrentAccount().getUserTags();
                Iterator iter = tags.iterator();
                while (iter.hasNext()) {
                    Tag tag = (Tag) iter.next();
                    trie.add(tag.toDisplayString(), tag);
                }

                iter = newTags.iterator();
                while (iter.hasNext()) {
                    Tag tag = (Tag) iter.next();
                    trie.add(tag.toDisplayString(), tag);
                }
            }
            initialized = true;
        }
    }

    public void verifyKey(VerifyEvent event) {
        StyledText txt = (StyledText) event.widget;
        event.doit = handleKeyPress(txt.getText(), event.keyCode, txt.getCaretOffset(), txt);
    }

    public void keyPressed(KeyEvent e) {
        Text txt = (Text) e.widget;
        e.doit = handleKeyPress(txt.getText(), e.keyCode, txt.getCaretPosition(), txt);
    }

    public void keyReleased(KeyEvent e) {

    }

    public boolean handleKeyPress(String txt, int keyCode, int caretPos, Widget widget) {

        boolean doIt = true;
        if (keyCode == SWT.CR || keyCode == SWT.LF) {
            doIt = false;
            int lastSpace = txt.lastIndexOf(' ', caretPos - 1);

            String word = txt.substring((lastSpace < 0) ? 0 : lastSpace, caretPos).trim();
            
            if (widget instanceof Text) {
                makePopup(word, (Text) widget);
            } else if (widget instanceof StyledText) {
                makePopup(word, (StyledText) widget, caretPos);
            }
        } else if (keyCode == ' ') {
            String input = txt;
            String word = "";
            Pattern lastQuoted = Pattern.compile(".*\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"$");
            Matcher matcher;
            if (!hasUnmatchedQuotes(input)) {
                if (input.trim().endsWith("\"")) {
                    matcher = lastQuoted.matcher(input);
                    if (matcher.matches()) {
                        word = matcher.group(1);
                    }
                } else {
                    String[] words = input.split(" ");
                    word = words[words.length - 1];
                }
            }

            if (word.trim().length() > 0) {
                Tag tag = new Tag(word);
                if (trie.get(tag.toDisplayString()) == null) {
                    trie.add(tag.toDisplayString(), tag);
                    newTags.add(tag);
                }
            }
        }
        return doIt;
    }

    private void makePopup(String baseWord, StyledText text, int pos) {
        String[] suggestions = getSuggestions(baseWord);
        if (suggestions != null && suggestions.length > 0) {
            PopupList list = new PopupList(text.getShell());
            list.setItems(suggestions);
            list.select(suggestions[0]);
            Rectangle cursorPosition = text.getClientArea();
            cursorPosition = text.getShell().getDisplay().map(text, null, cursorPosition);
            Rectangle initial = text.getBounds();
            Point location = text.getLocationAtOffset(pos);
            String selected = list.open(new Rectangle(cursorPosition.x + location.x, cursorPosition.y
                    - text.getBounds().height + location.y, setWidth(list), initial.height));
            if (selected != null) {
                String oldText = text.getText();
                int oldPosition = pos;
                StringBuffer newWord = new StringBuffer(selected);
                if (selected.indexOf(' ') >= 0) {
                    newWord.insert(0, '"');
                    newWord.append("\"");
                }

                if (StringUtils.isBlank(baseWord)) {
                    text.setText(oldText.substring(0, oldPosition) + " " + newWord.toString() + " "
                            + oldText.substring(oldPosition));
                    // //position at the end of the new word, plus the two
                    // spaces we added.
                    text.setSelection(oldPosition + newWord.length() + 2);
                } else {
                    text.setText(oldText.replaceAll("\\b" + baseWord + "\\b", newWord.toString()));
                    text.setSelection(oldPosition + (newWord.length() - baseWord.length()));
                }

            }
        }
    }

    private void makePopup(String baseWord, Text text) {
        String[] suggestions = getSuggestions(baseWord);
        if (suggestions != null && suggestions.length > 0) {
            PopupList list = new PopupList(text.getShell());
            list.setItems(suggestions);
            list.select(suggestions[0]);
            Rectangle cursorPosition = text.getClientArea();
            cursorPosition = text.getShell().getDisplay().map(text, null, cursorPosition);
            Rectangle initial = text.getBounds();
            String selected = list.open(new Rectangle(cursorPosition.x + text.getCaretLocation().x,
                    cursorPosition.y - text.getBounds().height + text.getCaretLocation().y, setWidth(list),
                    initial.height));
            if (selected != null) {
                String oldText = text.getText();
                int oldPosition = text.getCaretPosition();
                StringBuffer newWord = new StringBuffer(selected);
                if (selected.indexOf(' ') >= 0) {
                    newWord.insert(0, '"');
                    newWord.append("\"");
                }

                if (StringUtils.isBlank(baseWord)) {
                    text.setText(oldText.substring(0, oldPosition) + " " + newWord.toString() + " "
                            + oldText.substring(oldPosition));
                    // //position at the end of the new word, plus the two
                    // spaces we added.
                    text.setSelection(oldPosition + newWord.length() + 2);
                } else {
                    text.setText(oldText.replaceAll("\\b" + baseWord + "\\b", newWord.toString()));
                    text.setSelection(oldPosition + (newWord.length() - baseWord.length()));
                }

            }
        }
    }

    private String[] getSuggestions(String word) {
        List<String> suggestions = new LinkedList<String>();

        Iterator iter = trie.getPrefixedBy(word);
        while (iter.hasNext()) {
            Tag t = (Tag) iter.next();
            suggestions.add(t.toDisplayString());
        }
        return suggestions.toArray(new String[suggestions.size()]);
    }

    private int setWidth(PopupList l) {
        int wordLength = 0;
        int longestWord = 0;
        String[] words = l.getItems();
        if (words != null && words.length > 0) {
            for (int i = 0; i < words.length; i++) {
                String word = words[i];
                wordLength = Math.max(word.length(), wordLength);
                if (wordLength == word.length()) {
                    longestWord = i;
                }
            }

            GC gc = new GC(ReusableUIFactory.getInstance().getDisplay());
            wordLength = gc.textExtent(words[longestWord] + "  ").x;
            gc.dispose();
        }
        if (Configuration.isMac()) {
            wordLength += 50;
        }
        return wordLength;

    }

    private boolean hasUnmatchedQuotes(String s) {
        int quotes = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '"') {
                quotes++;
            }
        }
        return (quotes % 2) != 0;
    }

    public void updateTags(Set<Tag> toUpdate) {
        Iterator tags = toUpdate.iterator();
        while (tags.hasNext()) {
            Tag tag = (Tag) tags.next();
            if (trie.get(tag.toDisplayString()) == null) {
                newTags.add(tag);
                trie.add(tag.toDisplayString(), tag);
            }
        }
    }

}
